cmake_minimum_required(VERSION 3.17)
project(hMatMul CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS
    ON
    CACHE BOOL "used for clangd" FORCE)

# ------------------------------------------------------------------------------
# 自定义选项
# ------------------------------------------------------------------------------

# Enable verbose compiler's command line information
option(VERBOSE "cmake verbose" OFF)
set(CMAKE_VERBOSE_MAKEFILE ${VERBOSE})

# Choose the message passing interface
if(MPI_LIB)
  set(MPI_LIBS OPENMPI INTELMPI MVAPICH)
  list(FIND MPI_LIBS ${MPI_LIB} INDEX_FOUND)
  if(${INDEX_FOUND} EQUAL -1)
    message(FATAL_ERROR "MPI_LIB must be one of OPENMPI, INTELMPI or MVAPICH")
  endif()
else()
  # By default, use OpenMPI
  set(MPI_LIB
      "OPENMPI"
      CACHE STRING "the implementation of MPI")
endif()

# Choose the linear algebra library
if(PMAT_MUL_LIB)
  set(PMAT_MUL_LIBS MKL BLAS)
  list(FIND PMAT_MUL_LIBS ${PMAT_MUL_LIB} INDEX_FOUND)
  if(${INDEX_FOUND} EQUAL -1)
    message(FATAL_ERROR "PMAT_MUL_LIB must be one of MKL or BLAS")
  endif()
else()
  # By default, use MKL
  set(PMAT_MUL_LIB
      "MKL"
      CACHE STRING "the implementation of plaintext matrix multiplication")
endif()

# Enable Google Test
option(USE_GOOGLE_TEST "use googletest" OFF)

# Enable OpenMP user-defined reduction for helib::Ctxt
option(OMP_CTXT_REDUCTION
       "Enable OpenMP user-defined reduction for helib::Ctxt" OFF)

# Enable Intel ITT API
option(USE_INTEL_ITT "Intel Instrumentation and Tracing Technology APIs" OFF)

# ------------------------------------------------------------------------------
# 设置编译选项
# ------------------------------------------------------------------------------
set(CMAKE_CXX_STANDARD 17)

if(CMAKE_BUILD_TYPE)
  set(RELEASE_TYPES Debug Release RelWithDebInfo MinSizeRel)
  list(FIND RELEASE_TYPES ${CMAKE_BUILD_TYPE} INDEX_FOUND)

  if(${INDEX_FOUND} EQUAL -1)
    message(
      FATAL_ERROR
        "CMAKE_BUILD_TYPE must be one of Debug, Release, RelWithDebInfo, or MinSizeRel"
    )
  endif()
endif()

set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-gdwarf-3 -O3")
if(NOT CMAKE_BUILD_TYPE)
  # 默认Release
  set(CMAKE_BUILD_TYPE
      Release
      CACHE STRING "Build type" FORCE)
endif()

if(NOT MPI_LIB STREQUAL "INTELMPI")
  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=native")
endif()

# ------------------------------------------------------------------------------
# 输出状态
# ------------------------------------------------------------------------------
message(STATUS "CMAKE_BUILD_TYPE:              ${CMAKE_BUILD_TYPE}")
message(STATUS "CMAKE_CXX_COMPILER:            ${CMAKE_CXX_COMPILER}")
message(STATUS "CMAKE_VERBOSE_MAKEFILE:        ${VERBOSE}")
message(STATUS "MPI_LIBRARY:                   ${MPI_LIB}")
message(STATUS "PLAINTEXT_MAT_MUL:             ${PMAT_MUL_LIB}")
message(STATUS "USE_INTEL_ITT:                 ${USE_INTEL_ITT}")
message(STATUS "USE_GOOGLE_TEST:               ${USE_GOOGLE_TEST}")
message(STATUS "OMP_CTXT_REDUCTION:            ${OMP_CTXT_REDUCTION}")

# ------------------------------------------------------------------------------
# Third-party code...
# ------------------------------------------------------------------------------
find_package(OpenMP REQUIRED)

set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
find_package(NTL REQUIRED) # NTL
find_package(GMP REQUIRED) # GMP
find_package(MPI REQUIRED) # MPI

if(PMAT_MUL_LIB STREQUAL "MKL") # MKL
  # !!!!!!!!!!!!!!!!!!!!! If gnu openmp is mixed with intel openmp,
  # !!!!!!!!!!!!!!!!!!!!! MKL will generate incorrect answers
  set(MKL_THREADING gnu_thread)
  find_package(MKL CONFIG PATHS $ENV{MKLROOT})
  if(NOT MKL_FOUND)
    message(FATAL_ERROR "MKL: NOT found")
  endif()
  set_target_properties(MKL::MKL PROPERTIES IMPORTED_GLOBAL TRUE)
  add_library(MKL ALIAS MKL::MKL)
  get_target_property(MKL_INCLUDE_DIRS MKL::MKL INTERFACE_INCLUDE_DIRECTORIES)
endif()

if(USE_INTEL_ITT)
  find_package(ITT CONFIG)
  if(NOT ITT_FOUND)
    message(STATUS "Intel ITT API: install step")
    include(FetchContent)
    FetchContent_Declare(
      ittapi
      GIT_REPOSITORY https://github.com/intel/ittapi.git
      GIT_TAG master)
    FetchContent_MakeAvailable(ittapi)
    set(ittapi_INCLUDE_DIR "${ittapi_SOURCE_DIR}/include")
    message(STATUS "Intel ITT API: install step - Success")
  else()
    message(STATUS "Intel ITT API: found")
  endif()
  include_directories(${ittapi_INCLUDE_DIR})
  set(INTEL_ITT_READY
      ON
      CACHE BOOL "Intel ITT has been installed")
endif()

add_subdirectory(lib/helib) # HElib

# ------------------------------------------------------------------------------
# Including Header Files
# ------------------------------------------------------------------------------

# Project
include_directories(src)

# HElib
include_directories(lib/helib/include/helib)

# ------------------------------------------------------------------------------
# Project
# ------------------------------------------------------------------------------

function(create_target target source_list library_list)
  add_executable(${target} ${source_list})
  if(USE_INTEL_ITT)
    target_compile_options(${target} PRIVATE -DUSE_INTEL_ITT)
    target_link_libraries(${target} ittnotify)
  endif()

  list(FIND library_list "helib" INDEX_FOUND) # HElib
  if(NOT ${INDEX_FOUND} EQUAL -1)
    target_compile_options(${target} PRIVATE -fopenmp)
    if(OMP_CTXT_REDUCTION)
      target_compile_options(${target} PRIVATE -DOMP_CTXT_REDUCTION)
    endif()
    target_link_libraries(${target} helib ${OpenMP_CXX_LIBRARIES}
                          ${NTL_LIBRARY})
  endif()

  list(FIND library_list "mpi" INDEX_FOUND) # MPI
  if(NOT ${INDEX_FOUND} EQUAL -1)
    target_compile_options(${target} PRIVATE -DUSE_MPI_WTIME)
    target_link_libraries(${target} MPI::MPI_CXX)
  endif()

  string(TOLOWER ${PMAT_MUL_LIB} LOWER_PMM_LIB)
  list(FIND library_list ${LOWER_PMM_LIB} INDEX_FOUND) # Matrix Multiplication
  if(NOT ${INDEX_FOUND} EQUAL -1)
    target_compile_options(${target} PRIVATE -fopenmp)
    if(PMAT_MUL_LIB STREQUAL "MKL") # MKL
      target_compile_options(${target} PRIVATE -DUSE_INTEL_MKL)
      target_link_libraries(${target} MKL)
    endif()
  endif()

  list(FIND library_list "gtest" INDEX_FOUND) # Google Test
  if(NOT ${INDEX_FOUND} EQUAL -1)
    target_link_libraries(${target} gtest_main)
  endif()

endfunction()

add_subdirectory(test)
add_subdirectory(example)
